#ifndef __JSON_IO_CPP__
#define __JSON_IO_CPP__

#include <fstream>
#include <iomanip>
#include "jsonxx/json.hpp"
#include "parser_types.cpp"

using namespace jsonxx;

#define OPEN_AND_CHECK(s, name, output)                                                            \
    if (output)                                                                                    \
        s.open(name, std::fstream::in | std::fstream::out | std::fstream::trunc);                  \
    else                                                                                           \
        s.open(name);                                                                              \
    if (!s.is_open()) {                                                                            \
        cout << "Fail to open" << name << endl;                                                    \
        exit(0);                                                                                   \
    }

template<>
struct json_bind<PtrASTNode>{
    const string NAME = "Name";
    const string CHILDREN = "children";
    void to_json(json& j, const PtrASTNode& ast){
        jsonxx::to_json(j[NAME], ast->name);
        for(auto [key, val]: ast->fields){
            string str;
            for(auto j: val)
                str += j.word;
            jsonxx::to_json(j[key], str);
        }
        vector<json> children;
        for (auto i = ast->children.begin(); i != ast->children.end(); ++i) {
            json tmp;
            to_json(tmp, *i);
            children.push_back(tmp);
        }
        if(!children.empty())
            j[CHILDREN] = children;
    }

    void from_json(const json& j, PtrASTNode& ast){
        jsonxx::from_json(j[NAME], ast->name);
        vector<json> children;
        if(j.count(CHILDREN))
            children = j[CHILDREN].as_array();
        for(auto i: children){
            PtrASTNode tmp = make_shared<ASTNode>();
            from_json(i, tmp);
            ast->children.push_back(tmp);
        }
        for(auto [key, val]: j.as_object()){
            if (key != CHILDREN) { // 处理fields
                Token token;
                token.word = val.as_string();
                ast->fields[key].push_back(token);
            }
        }
    }
};

void initializeASTNodeWithJson(const string &path) {
    std::ifstream fin;
    fin.open(path);
    OPEN_AND_CHECK(fin, path, false);
    json jd;
    fin >> jd;
    auto &&ignore = jd["ignoredTerminals"].as_array();
    ASTNode::ignoredTerminals = {ignore.begin(), ignore.end()};

    auto &&c2f = jd["childToFields"].as_array();
    ASTNode::childToFields = {c2f.begin(), c2f.end()};

    auto &&c2s = jd["childToSiblings"].as_array();
    ASTNode::childToSibling = {c2s.begin(), c2s.end()};

    auto &&mc = jd["mergeChilds"].as_array();
    ASTNode::mergeChilds = {mc.begin(), mc.end()};

    auto &&nameMap = jd["terminalNamesMapping"];
    for (auto iter = nameMap.begin(); iter != nameMap.end(); iter++) {
        for (auto terminal = iter.value().begin(); terminal != iter.value().end(); ++terminal) {
            ASTNode::terminalNameMap.insert({terminal.value().as_string(), iter.key()});
        }
    }

    auto &&rules = jd["parseTreeToASTRules"].as_array();
    for (auto i : rules) {
        if (i["type"] == "Generalization") {
            auto targets = i["targets"];
            for (auto j : targets) {
                ASTNode::generalization.insert({j.as_string(), i["category"].as_string()});
            }
            if (i.count("compressExclusions")) {
                ASTNode::compressExclusion = {i["compressExclusions"].as_array().begin(),
                                              i["compressExclusions"].as_array().end()};
            }
        }
    }
    cout << "Initialized AST static fields\n";
}


int mainasd(){
    json j;
    cout << std::setw(2) << "Yeah\n\n";
    cin >> j;
    cout << endl << endl << j << endl << endl;
    PtrASTNode tmp = make_shared<ASTNode>();
    from_json(j, tmp);
    to_json(j, tmp);
    cout << j << endl;
    return 0;
}


#endif